﻿using Server.Manager;
using Server.Struct;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace Server.DTool
{
    class TimeTaskScheduler
    {
        public readonly static int TIME_INTERVAL = 20;

        Timer _timer;

        Pool<List<TimeTask>> _ttListPool;

        System.Threading.Semaphore _aryLocker;

        RootHeap<TimeTask> _heap;

        public TimeTaskScheduler()
        {
            _heap = new RootHeap<TimeTask>((x, y) =>
            {
                return x.ToTicks < y.ToTicks;
            });

            _ttListPool = new Pool<List<TimeTask>>(() => { return new List<TimeTask>(2); });

            _timer = new Timer(TIME_INTERVAL);
            _timer.Elapsed += Execute;

            _aryLocker = new System.Threading.Semaphore(1, 1);
        }

        public void AddTask(Action task, int intervalMs)
        {
            AddTask(task, intervalMs, 1);
        }

        public void AddTask(Action task, int intervalMs, int loopTimes)
        {
            if (loopTimes == 0) return;
            _aryLocker.WaitOne();

            TimeTask tt = TimeTask.GetTimeTask(task, intervalMs, loopTimes);
            _heap.Push(tt);

            _aryLocker.Release();
        }

        public void AddTask(TimeTask task)
        {
            if (task.LoopTimes == 0) return;
            _aryLocker.WaitOne();
            _heap.Push(task);
            _aryLocker.Release();
        }

        void Execute(object sender, ElapsedEventArgs e)
        {
            _aryLocker.WaitOne();

            if (_heap.IsEmpty)
            {
                _aryLocker.Release();
                return;
            }
            Action DO = null;

            List<TimeTask> listTT = _ttListPool.Take();
            long nowTicks;
            do
            {
                nowTicks = DateTime.UtcNow.Ticks;
                TimeTask tt = _heap.Peek();
                if (nowTicks >= tt.ToTicks)
                {
                    _heap.Pop();

                    DO = (Action)Delegate.Combine(DO, tt.Task);

                    tt.LoopTimes--;
                    //Console.WriteLine("Execute111: TO: {0} Cur: {1} Delta:{2}", tt.ToTicks, ticks, tt.ToTicks - ticks);
                    if (tt.LoopTimes > 0)
                    {
                        tt.ToTicks = TimeSystem.IncTicks(nowTicks, tt.Interval);
                        //Console.WriteLine("Execute2222: TO: {0} Cur: {1} Delta:{2}", tt.ToTicks, ticks, tt.ToTicks - ticks);
                        listTT.Add(tt);
                    }
                    else
                    {
                        TimeTask.RecycleTimeTask(tt);
                    }
                }
                else
                    break;
            } while (!_heap.IsEmpty);

            foreach (var item in listTT)
            {
                _heap.Push(item);
            }

            listTT.Clear();
            _ttListPool.Recycle(listTT);
            _aryLocker.Release();

            DO?.Invoke();
        }

        public void Start()
        {
            _timer.Enabled = true;
        }

        public void Stop()
        {
            _timer.Enabled = false;
        }

        public void Clear()
        {
            Stop();
            _heap.Clear();
        }
    }

    class TimeTask
    {
        public long Interval { get; set; }
        public long ToTicks { get; set; }
        public Action Task { get; set; }
        public int LoopTimes { get; set; }

        static SyncPool<TimeTask> _pool = new(()=> { return new TimeTask(); });

        public static TimeTask GetTimeTask()
        {
            if (_pool.TryTake(out TimeTask task))
            {
                return task;
            }
            return null;
        }

        public static TimeTask GetTimeTask(Action action, int intervalMs, int loopTimes)
        {
            if(_pool.TryTake(out TimeTask task))
            {
                task.Interval = intervalMs;
                task.ToTicks = TimeSystem.NextTicks(intervalMs);
                task.LoopTimes = loopTimes;
                task.Task = action;
                return task;
            }
            return null;
        }

        public static void  RecycleTimeTask(TimeTask timeTask)
        {
            _pool.Recycle(timeTask);
        }
    }
}
